#ifndef _PAGER_ 
#define _PAGER_ 1

#include <cerrno>
#include <unistd.h>
#include <fcntl.h>

#include "row.h"

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

const uint32_t PAGE_SIZE = 4096;
#define TABLE_MAX_PAGES 100
const uint32_t ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE;
const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;

class Pager {
public:
    Pager(const char* filename);
    ~Pager();
    bool        isNull(int pos) { return pages[pos] == nullptr; }
    uint32_t    getFileLength() { return file_length; }
    uint32_t    getNumPages()   { return num_pages; }
    void*       getPage(uint32_t page_num);
    void        flush(uint32_t page_num);
private:
    FILE*       file_descriptor;
    uint32_t    file_length;
    uint32_t    num_pages;
    void*       pages[TABLE_MAX_PAGES] { nullptr };
};

Pager::Pager(const char* filename) {
    FILE* fd = fopen(filename, "rb+");
    if (fd == NULL) {
        fd = fopen(filename, "wb+");
        if (fd == NULL) {
            printf("Unable to open file\n");
            exit(EXIT_FAILURE);
        }
        printf("Created '%s'\n", filename);
    }
    fseek(fd, 0, SEEK_END);
    file_length = ftell(fd);
    fseek(fd, 0, SEEK_SET);

    printf("Open a file whose size is %d.\n", file_length);
    if (file_length % PAGE_SIZE != 0) {
        printf("Db file is not a whole number og pages. Corrupt file.\n");
        exit(EXIT_FAILURE);
    }

    num_pages = file_length / PAGE_SIZE;

    file_descriptor = fd;
}

Pager::~Pager() {
    int result = fclose(file_descriptor);
    if (result == -1) {
        printf("Error closing db file.\n");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < TABLE_MAX_PAGES; i++) 
        if (pages[i] != nullptr) 
            delete[] static_cast<char(* )[PAGE_SIZE]>(pages[i]);
}

void* Pager::getPage(uint32_t page_num) {
    if (page_num > TABLE_MAX_PAGES) {
        printf("Tried to fetch page number out of bounds. %d > %d\n", 
                                                    page_num, TABLE_MAX_PAGES);
        exit(EXIT_FAILURE);
    }

    if (pages[page_num] == nullptr) {
        // Cache miss. Allocate memory and load from file.
        void* page = new char[PAGE_SIZE];
        uint32_t num_pages = file_length / PAGE_SIZE;

        if (file_length % PAGE_SIZE != 0) {
            num_pages++;
        }

        if (page_num <= num_pages) {
            fseek(file_descriptor, page_num * PAGE_SIZE, SEEK_SET);
            ssize_t bytes_read = fread(page, PAGE_SIZE, 1, file_descriptor);
            if (file_length != 0 && bytes_read == 0) {
                printf("Error reading file: %d\n", errno);
                exit(EXIT_FAILURE);
            }
        }

        pages[page_num] = page;
        printf("Had load page %d in pos %p\n", page_num, page);

        if (page_num >= num_pages) {
            num_pages = page_num + 1;
        }
    }
    return pages[page_num];
}

// 将内存中的数据同步到文件中
void Pager::flush(uint32_t page_num) {
    if (pages[page_num] == nullptr) {
        printf("Tried to flush null page\n");
        exit(EXIT_FAILURE);
    }
    off_t offset = fseek(file_descriptor, page_num * PAGE_SIZE, SEEK_SET);

    if (offset != 0) {
        printf("Error seeking: %d\n", errno);
        exit(EXIT_FAILURE);
    }
    ssize_t bytes_written = fwrite(pages[page_num], PAGE_SIZE, 1, file_descriptor);

    if (bytes_written == -1) {
        printf("Error writing: %d\n", errno);
        exit(EXIT_FAILURE);
    }

    return ;
}

#endif